int intr_type = APIC_DM_EXTINT;
int intr_vector = -1;
- /*
- * Do not deliver a virtual interrupt (vintr) if an exception is pending.
- * This is because the delivery of the exception can arbitrarily delay
- * the injection of the vintr (for example, if the exception is handled
- * via an interrupt gate, hence zeroing RFLAGS.IF). In the meantime the
- * vTPR can be modified upwards and we can end up delivering the vintr
- * when it is not in fact valid to do so (because we do not re-check the
- * vTPR value). Moreover, the guest will be able to see the updated
- * APIC/PIC state (as if the interrupt had been acknowledged) yet will not
- * have actually received the interrupt. This could confuse the guest!
- */
- if ( vmcb->eventinj.fields.v )
- return;
-
/*
* Previous Interrupt delivery caused this intercept?
* This will happen if the injection is latched by the processor (hence
return;
/*
- * Create a 'fake' virtual interrupt on to intercept as soon as the
- * guest _can_ take interrupts. Do not obtain the next interrupt from
- * the vlapic/pic if unable to inject.
+ * If the guest can't take an interrupt right now, create a 'fake'
+ * virtual interrupt on to intercept as soon as the guest _can_ take
+ * interrupts. Do not obtain the next interrupt from the vlapic/pic
+ * if unable to inject.
+ *
+ * Also do this if there is an exception pending. This is because
+ * the delivery of the exception can arbitrarily delay the injection
+ * of the vintr (for example, if the exception is handled via an
+ * interrupt gate, hence zeroing RFLAGS.IF). In the meantime:
+ * - the vTPR could be modified upwards, so we need to wait until the
+ * exception is delivered before we can safely decide that an
+ * interrupt is deliverable; and
+ * - the guest might look at the APIC/PIC state, so we ought not to have
+ * cleared the interrupt out of the IRR.
*/
- if ( irq_masked(vmcb->rflags) || vmcb->interrupt_shadow )
+ if ( irq_masked(vmcb->rflags) || vmcb->interrupt_shadow
+ || vmcb->eventinj.fields.v )
{
vmcb->general1_intercepts |= GENERAL1_INTERCEPT_VINTR;
HVMTRACE_2D(INJ_VIRQ, v, 0x0, /*fake=*/ 1);